package com.pro.common.service.pay.service.unit;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.pro.common.api.enums.init.EnumCountry;
import com.pro.common.api.enums.init.EnumCurrency;
import com.pro.common.api.model.LoginInfo;
import com.pro.common.api.model.request.pay.*;
import com.pro.common.api.pay.model.db.PaymentOrder;
import com.pro.common.api.pay.model.db.PayMerchant;
import com.pro.common.api.pay.model.db.PaymentChannel;
import com.pro.common.api.pay.model.dto.PaymentParamDTO;
import com.pro.common.service.pay.service.PayChannelService;
import com.pro.common.service.pay.service.PayMerchantService;
import com.pro.common.service.pay.service.PaymentOrderService;
import com.pro.common.api.pay.intf.IPaymentService;
import com.pro.common.service.service.EnumAuthDict;
import com.pro.framework.api.util.AssertUtil;
import com.pro.framework.api.util.CollUtils;
import com.pro.framework.api.util.LogicUtils;
import com.pro.framework.api.util.StrUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

import static com.pro.framework.api.FrameworkConst.DateTimes.DATE_TIME_FORMAT_DATE;
import static com.pro.framework.api.FrameworkConst.DateTimes.DATE_TIME_FORMAT_DATE_SIMPLE;

@Slf4j
@Service
public class PaymentUnitService {
    @Autowired
    private PaymentOrderService paymentOrderService;
    @Autowired
    private PayChannelService channelService;
    @Autowired
    private PayMerchantService merchantService;


    private Map<String, IPaymentService> paymentServiceMap;

    @Autowired(required = false)
    public void setPaymentServiceMap(List<IPaymentService> paymentServices) {
        this.paymentServiceMap = CollUtils.listToMap(paymentServices, IPaymentService::getMerchantPlatform);
    }

    /**
     * 发起支付
     */
    public PaymentResponse payment(PaymentRequest request) {
        LoginInfo loginInfo = request.getLoginInfo();
        PaymentParamsDTO paymentParamsDTO = request.getPaymentParamsDTO();
        PaymentUserDetail userDetail = request.getUserDetail();
        String ip = request.getIp();
        PaymentChannel channel = channelService.getById(paymentParamsDTO.getPayChannelId());
        AssertUtil.notEmpty(channel, "请先选择支付方式");
        AssertUtil.isTrue(channel.getEnabled(), "请先选择支付方式");

        PayMerchant merchant = merchantService.getByCode(channel.getMerchantCode());
        AssertUtil.notEmpty(merchant, "请先选择支付商户");
        String merchantPlatform = merchant.getPlatform();

        IPaymentService paymentService = paymentServiceMap.get(merchantPlatform);
        AssertUtil.notEmpty(paymentService, "请先对接通道_实现IPaymentService_", merchantPlatform);

        // 创建支付订单
        PaymentOrder paymentOrder = paymentOrderService.save(loginInfo, paymentParamsDTO, channel, merchant);

        EnumCurrency currency = channel.getCurrency();
        Date now = new Date();

        String returnUrl = paymentParamsDTO.getReturnUrl(); //http://localhost:5202/buy/buycenter
        if (returnUrl == null) {
            returnUrl = EnumAuthDict.FRONT_DOMAIN.getValueCacheOrDefault("");
        }

        String merchantCode = merchant.getCode();
        PaymentParamDTO dto = PaymentParamDTO.builder()
                .notifyUrl(EnumAuthDict.PAY_NOTIFY_URL.getValueCacheOrDefault("") + "/api/pay/payment/" + merchantCode + "/notify")
                .returnUrl(returnUrl)
                .now_millis(now.getTime())
                .now_yyyy_MM_dd_HH_mm_ss(DATE_TIME_FORMAT_DATE.format(now))
                .now_yyyyMMddHHmmss(DATE_TIME_FORMAT_DATE_SIMPLE.format(now))
                .no(paymentOrder.getNo())
                .orderAmount(paymentOrder.getOrderAmount())
                .ip(ip)
                .subject(paymentParamsDTO.getSubject())
                .currency(currency)
                .payMethod(channel.getPayMethod())
                // 读取合适的国家
                .country(getCountry(LogicUtils.and(userDetail, PaymentUserDetail::getCountry), channel.getCountries(), currency.getCountries()))
                .build();

        // 调用具体三方实现,发起代收
        return paymentService.payment(dto, userDetail, merchant);
    }

    /**
     * 代收回调
     *
     * @param merchantCode 商户编号
     * @param params       所有参数
     */
    @SneakyThrows
    public Object paymentNotify(String merchantCode, String params) {
        PayMerchant merchant = merchantService.getByCode(merchantCode);
        AssertUtil.notEmpty(merchant, "商户不存在_", merchantCode);
        IPaymentService paymentService = paymentServiceMap.get(merchant.getPlatform());
        log.warn("=== paymentNotify start === {} {}", merchantCode, params);
        try {
            // 代收回调验证并读取信息
            PaymentNotifyData data = paymentService.paymentNotifyRead(params, merchant);
            boolean successFlag = BooleanUtil.isTrue(data.getStatusSuccess());
            boolean failFlag = BooleanUtil.isTrue(data.getStatusFail());
            boolean end = LogicUtils.or(successFlag, failFlag);
            if (end) {
                // 代收回调返回
                paymentOrderService.paymentEnd(data.getNo(), data.getPayAmount(), data.getOutId(), successFlag, merchant);
            }
            return paymentService.paymentNotifyReturn(end);
        } catch (Exception e) {
            log.warn("=== paymentNotify error === {} {}", merchantCode, params, e);
            throw e;
        }
    }

    /**
     * 读取合适的国家
     *
     * @param countryUser        用户对应国家
     * @param countryStrChannel  渠道设置国家
     * @param countryStrCurrency 渠道货币对应国家
     */
    private static EnumCountry getCountry(EnumCountry countryUser, String countryStrChannel, String countryStrCurrency) {
        List<EnumCountry> countries = new ArrayList<>();
        List<EnumCountry> countriesChannel = null == countryStrChannel ? Collections.emptyList() : StrUtils.split(countryStrChannel, EnumCountry.class);
        List<EnumCountry> countriesCurrency = null == countryStrCurrency ? Collections.emptyList() : StrUtils.split(countryStrCurrency, EnumCountry.class);
        //noinspection ConstantValue
        if (countries.isEmpty() && !countriesChannel.isEmpty()) {
            countries = countriesChannel;
        }
        if (countries.isEmpty() && !countriesCurrency.isEmpty()) {
            countries = countriesCurrency;
        }
        if (countries.contains(countryUser)) {
            return countryUser;
        }
        return countries.get(0);
    }

}
